﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace SecureCommander.Core.SecurityManager
{
    public class RijndaelAlgorithm
    {
        private Rijndael Rij;// = Rijndael.Create();
        public byte[] Key;
        public byte[] IV;
        public RijndaelAlgorithm()
        {
            Rij = Rijndael.Create();
            //Rij.KeySize = 128;
            //Rij.BlockSize = 128;
            Rij.Padding = PaddingMode.PKCS7;
            Rij.Mode = CipherMode.ECB;
            Rij.GenerateIV();
        }
        public RijndaelAlgorithm(PaddingMode pad, CipherMode cipher, byte[] Key, byte[] IV)
        {
            Rij = Rijndael.Create();
            Rij.IV = IV;
            Rij.Key = Key;
            Rij.Padding = pad;
            Rij.Mode = cipher;
        }

        public RijndaelAlgorithm(int keysize, int blocksize, PaddingMode pad, CipherMode cipher, byte[] iv)
        {
            Rij = Rijndael.Create();
            Rij.KeySize = keysize;
            Rij.BlockSize = blocksize;
            Rij.Padding = pad;
            Rij.Mode = cipher;
            Rij.IV = iv;
        }

        public RijndaelAlgorithm(PaddingMode pad, CipherMode cipher)
        {
            Rij = Rijndael.Create();
            Rij.GenerateIV();
            IV = Rij.IV;
            Rij.GenerateKey();
            Key = Rij.Key;
            Rij.Padding = pad;
            Rij.Mode = cipher;
        }

        public String Encrypt(String data, String key)
        {
            try
            {
                byte[] bKey = ASCIIEncoding.Default.GetBytes(key);
                byte[] input = ASCIIEncoding.Default.GetBytes(data);
                Rij.Key = GetValidKey(key);
                MemoryStream outStream = new MemoryStream();
                CryptoStream crStream = new CryptoStream(outStream, Rij.CreateEncryptor(), CryptoStreamMode.Write);
                crStream.Write(input, 0, input.Length);
                crStream.FlushFinalBlock();
                crStream.Close();
                String rs = Convert.ToBase64String(outStream.ToArray());
                outStream.Close();
                return rs;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public String Decrypt(String data, String key)
        {
            try
            {
                byte[] bKey = ASCIIEncoding.Default.GetBytes(key);
                byte[] input = Convert.FromBase64String(data);
                Rij.Key = GetValidKey(key);
                MemoryStream outStream = new MemoryStream();
                CryptoStream crStream = new CryptoStream(outStream, Rij.CreateDecryptor(), CryptoStreamMode.Write);
                crStream.Write(input, 0, input.Length);
                crStream.FlushFinalBlock();
                crStream.Close();
                outStream.Close();
                return ASCIIEncoding.Default.GetString(outStream.ToArray());
            }
            catch (Exception)
            {
                throw;
            }
        }


        public byte[] GetValidKey(string Key)
        {
            if (Rij.LegalKeySizes.Length > 0)
            {
                int keySize = Key.Length * 8;
                int minSize = Rij.LegalKeySizes[0].MinSize;
                int maxSize = Rij.LegalKeySizes[0].MaxSize;
                int skipSize = Rij.LegalKeySizes[0].SkipSize;

                if (keySize > maxSize)
                {
                    Key = Key.Substring(0, maxSize / 8);
                }
                else if (keySize < maxSize)
                {
                    int validSize = (keySize <= minSize) ? minSize :
                         (keySize - keySize % skipSize) + skipSize;
                    if (keySize < validSize)
                    {
                        Key = Key.PadRight(validSize / 8, '@');
                    }
                }
            }
            return ASCIIEncoding.Default.GetBytes(Key);
        }

        public bool EncryptFile(String nameFileInput, String nameFileOutput)
        {

            FileStream in_fStream = File.Open(nameFileInput, FileMode.Open, FileAccess.Read);
            FileStream out_fStream = File.Open(nameFileOutput, FileMode.OpenOrCreate, FileAccess.Write);
            try
            {
                CryptoStream cStream = new CryptoStream(out_fStream, Rij.CreateEncryptor(), CryptoStreamMode.Write);

                BinaryWriter sWriter = new BinaryWriter(cStream);
                in_fStream.Close();

                byte[] input;
                input = File.ReadAllBytes(nameFileInput);
                try
                {
                    sWriter.Write(input);
                }
                catch (Exception)
                {
                    return false;
                }

                sWriter.Close();
                cStream.Close();
            }
            catch (CryptographicException)
            {
                return false;
            }
            finally
            {
                out_fStream.Close();
            }
            return true;
        }


        public bool DecryptFile(String nameFileInput, String nameFileOutput)
        {
            FileStream in_fStream = File.Open(nameFileInput, FileMode.Open, FileAccess.Read);
            FileStream out_fStream = File.Open(nameFileOutput, FileMode.OpenOrCreate, FileAccess.Write);
            try
            {                
                CryptoStream cStream = new CryptoStream(out_fStream, Rij.CreateDecryptor(), CryptoStreamMode.Write);

                BinaryWriter sWriter = new BinaryWriter(cStream);
                in_fStream.Close();
                byte[] input;
                input = File.ReadAllBytes(nameFileInput);
                try
                {
                    sWriter.Write(input);
                }
                catch (Exception)
                {
                    return false;
                }
                sWriter.Close();
                cStream.Close();
            }
            catch (CryptographicException)
            {
                return false;
            }
            finally
            {
                out_fStream.Close();
            }

            return true;
        }
    }
}
